package com.eunion.fileserver.upload;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 图片压缩工具类.
 * http://blog.csdn.net/hu_shengyang/article/details/7433988
 * @version 2016年11月24日
 */
public class ImageCompressUtil {

	static Logger LOG = LoggerFactory.getLogger(ImageCompressUtil.class);

	static int MIN_WIDTH_HEIGHT = 600;//最小高度.

	public static void main(String[] args) {
		//201610150101011397.jpg
		//compressAutoWidthHeight(new File("E:\\Ken\\cw\\img\\201610150101011357.jpg"));
		compressAutoWidthHeight(new File("E:\\Ken\\cw\\img\\201610150101011397.jpg"));
		//compressAutoWidthHeight(new File("E:\\Ken\\cw\\img\\201610150101011357.jpg"), new File("E:\\Ken\\cw\\img\\201610150101011357_000.jpg"), 200, 200, 1f);
	}

	/**  
	 * 根据设置的宽高等比例压缩图片文件<br> 先保存原文件，再压缩、上传  
	 * @param oldFile  要进行压缩的文件  
	 * @param width  宽度 //设置宽度时（高度传入0，等比例缩放）  
	 * @param height 高度 //设置高度时（宽度传入0，等比例缩放）  
	 * @param quality 质量  
	 * @return 返回压缩后的文件的全路径  
	 */
	public static String compressDefineWidthHeight(File oldFile, int width, int height, float quality) {
		if (oldFile == null) {
			return null;
		}
		try {
			/** 对服务器上的临时文件进行处理 */
			Image srcFile = ImageIO.read(oldFile);
			int w = srcFile.getWidth(null);
			int h = srcFile.getHeight(null);
			double bili;
			if (width > 0) {
				bili = width / (double) w;
				height = (int) (h * bili);
			} else {
				if (height > 0) {
					bili = height / (double) h;
					width = (int) (w * bili);
				}
			}
			boolean flag = compress(srcFile, oldFile.getAbsolutePath(), width, height);
			if (flag) {
				return getCompressFileNamePath(oldFile.getAbsolutePath());
			}
		} catch (FileNotFoundException e) {
			LOG.error("图片压缩，图片未找到.", e);
		} catch (IOException e) {
			LOG.error("图片压缩失败.", e);
		}
		return oldFile.getAbsolutePath();
	}

	/**
	 * 获取文件的缩略名字.
	 * @param pathName 原文件名.
	 * @return 缩略名.
	 */
	public static String getCompressFileNamePath(String pathName) {
		String subfix = pathName.substring(pathName.lastIndexOf(".") + 1, pathName.length());
		String tmp = pathName + "._min_." + subfix;
		LOG.debug("原名字：{}， 压缩后名字{} ", pathName, tmp);
		return tmp;
	}

	/**
	 * 压缩图片.
	 * @param srcFile 待压缩图片
	 * @param srcFullPathName 待压缩图片全路径
	 * @param width 宽度
	 * @param height 高度
	 * @return 返回状态.
	 */
	private static boolean compress(Image srcFile, String srcFullPathName, int width, int height) {
		try {
			String subfix = srcFullPathName.substring(srcFullPathName.lastIndexOf(".") + 1, srcFullPathName.length());
			String tmp = getCompressFileNamePath(srcFullPathName);
			BufferedImage buffImg = null;
			if (subfix.equals("png")) {
				buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			} else {
				buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			}

			Graphics2D graphics = buffImg.createGraphics();
			graphics.setBackground(new Color(255, 255, 255));
			graphics.setColor(new Color(255, 255, 255));
			graphics.fillRect(0, 0, width, height);
			graphics.drawImage(srcFile.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
			ImageIO.write(buffImg, subfix, new File(tmp));
			return true;
		} catch (FileNotFoundException e) {
			LOG.error("图片压缩，失败.", e);
		} catch (IOException e) {
			LOG.error("图片压缩失败.", e);
		}
		return false;
	}

	/**
	 * 压缩图片.
	 * @param srcFile 待压缩图片
	 * @param srcFullPathName 待压缩图片全路径
	 * @param width 宽度
	 * @param height 高度
	 * @return 返回状态.
	 */
	private static OutputStream compressInputStream(Image srcFile, String subfix, int width, int height) {
		try {
			BufferedImage buffImg = null;
			if (subfix.equals("png")) {
				buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			} else {
				buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			}

			Graphics2D graphics = buffImg.createGraphics();
			graphics.setBackground(new Color(255, 255, 255));
			graphics.setColor(new Color(255, 255, 255));
			graphics.fillRect(0, 0, width, height);
			graphics.drawImage(srcFile.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
			OutputStream out = new ByteArrayOutputStream();
			ImageIO.write(buffImg, subfix, out);
			return out;
		} catch (FileNotFoundException e) {
			LOG.error("图片压缩，失败.", e);
		} catch (IOException e) {
			LOG.error("图片压缩失败.", e);
		}
		return null;
	}

	/**  
	 * 按设置的宽度高度压缩图片文件<br> 先保存原文件，再压缩、上传  
	 * @param oldFile  要进行压缩的文件全路径  
	 * @param newFile  新文件  
	 * @param width  宽度  
	 * @param height 高度  
	 * @param quality 质量  
	 * @return 返回压缩后的文件的全路径  
	 */
	public static String compressAutoWidthHeight(File oldFile) {
		if (oldFile == null || !oldFile.exists()) {
			return null;
		}
		try {
			/** 对服务器上的临时文件进行处理 */
			//Image srcFile = ImageIO.read(oldFile);
			BufferedImage srcFile = ImageIO.read(new FileInputStream(oldFile));
			int width = srcFile.getWidth();
			int height = srcFile.getHeight();
			LOG.debug("width:{},height : {} ", srcFile.getWidth(), srcFile.getHeight());
			int widthR = width / MIN_WIDTH_HEIGHT;
			int heightR = height / MIN_WIDTH_HEIGHT;
			int rate = widthR;
			if (rate > heightR) {
				rate = heightR;
			}
			if (rate <= 2) {
				rate = 2;
			}
			width = width / rate;
			height = height / rate;
			LOG.debug("width:{},height : {},rate:{}, new width:{},new height:{}", srcFile.getWidth(), srcFile.getHeight(), rate, width, height);

			boolean flag = compress(srcFile, oldFile.getAbsolutePath(), width, height);
			if (flag) {
				return getCompressFileNamePath(oldFile.getAbsolutePath());
			}
		} catch (FileNotFoundException e) {
			LOG.error("图片压缩，图片未找到.", e);
		} catch (IOException e) {
			LOG.error("图片压缩失败.", e);
		}
		return oldFile.getAbsolutePath();
	}

	/**
	* 从内存字节数组中读取图像
	* http://blog.csdn.net/10km/article/details/52119508
	* @param imgBytes
	*            未解码的图像数据
	* @return 返回 {@link BufferedImage}
	* @throws IOException
	*             当读写错误或不识别的格式时抛出
	*/
	public static final BufferedImage readMemoryImage(byte[] imgBytes)
	    throws IOException {
		if (null == imgBytes || 0 == imgBytes.length) throw new NullPointerException("the argument 'imgBytes' must not be null or empty");
		// 将字节数组转为InputStream，再转为MemoryCacheImageInputStream
		ImageInputStream imageInputstream = new MemoryCacheImageInputStream(new ByteArrayInputStream(imgBytes));
		// 获取所有能识别数据流格式的ImageReader对象
		Iterator<ImageReader> it = ImageIO.getImageReaders(imageInputstream);
		// 迭代器遍历尝试用ImageReader对象进行解码
		while (it.hasNext()) {
			ImageReader imageReader = it.next();
			// 设置解码器的输入流
			imageReader.setInput(imageInputstream, true, true);
			// 图像文件格式后缀
			String suffix = imageReader.getFormatName().trim().toLowerCase();
			// 图像宽度
			int width = imageReader.getWidth(0);
			// 图像高度
			int height = imageReader.getHeight(0);
			System.out.printf("format %s,%dx%d\n", suffix, width, height);
			try {
				// 解码成功返回BufferedImage对象
				// 0即为对第0张图像解码(gif格式会有多张图像),前面获取宽度高度的方法中的参数0也是同样的意思
				return imageReader.read(0, imageReader.getDefaultReadParam());
			} catch (Exception e) {
				imageReader.dispose();
				// 如果解码失败尝试用下一个ImageReader解码
			}
		}
		imageInputstream.close();
		// 没有能识别此数据的图像ImageReader对象，抛出异常
		throw new IOException("unsupported image format");
	}

	/**
	 * 从{@link InputStream}读取字节数组<br>
	 * 结束时会关闭{@link InputStream}<br>
	 * {@code in}为{@code null}时抛出{@link NullPointerException}
	 * 
	 * @param in
	 * @return 字节数组
	 * @throws IOException
	 */
	public static final byte[] readBytes(InputStream in)
	    throws IOException {
		if (null == in) throw new NullPointerException("the argument 'in' must not be null");
		try {
			int buffSize = Math.max(in.available(), 1024 * 8);
			byte[] temp = new byte[buffSize];
			ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);
			int size = 0;
			while ((size = in.read(temp)) != -1) {
				out.write(temp, 0, size);
			}
			return out.toByteArray();
		} finally {
			//            in.close();
		}
	}

	public static final InputStream byte2Input(byte[] buf) {
		return new ByteArrayInputStream(buf);
	}

	/**  
	 * 按设置的宽度高度压缩图片文件<br> 先保存原文件，再压缩、上传  
	 * @param oldFile  要进行压缩的文件全路径  
	 * @param newFile  新文件  
	 * @param width  宽度  
	 * @param height 高度  
	 * @param quality 质量  
	 * @return 返回压缩后的文件的全路径  
	 */
	public static InputStream compressAutoWidthHeight(byte[] inputBytes, String subfix) {
		if (inputBytes == null || inputBytes.length < 1) {
			return null;
		}
		try {
			/** 对服务器上的临时文件进行处理 */
			//			byte[] inputBytes = readBytes(oldFile);
			BufferedImage srcFile = readMemoryImage(inputBytes);
			int width = srcFile.getWidth();
			int height = srcFile.getHeight();
			LOG.debug("width:{},height : {} ", srcFile.getWidth(), srcFile.getHeight());
			int widthR = width / MIN_WIDTH_HEIGHT;
			int heightR = height / MIN_WIDTH_HEIGHT;
			int rate = widthR;
			if (rate > heightR) {
				rate = heightR;
			}
			if (rate <= 2) {
				rate = 2;
			}
			width = width / rate;
			height = height / rate;
			LOG.debug("width:{},height : {},rate:{}, new width:{},new height:{}", srcFile.getWidth(), srcFile.getHeight(), rate, width, height);

			OutputStream out = compressInputStream(srcFile, subfix, width, height);
			return getInputFromOutPutStream(out);
		} catch (FileNotFoundException e) {
			LOG.error("图片压缩，图片未找到.", e);
		} catch (IOException e) {
			LOG.error("图片压缩失败.", e);
		}
		return null;
	}

	/**
	 * outputStream转inputStream.
	 */
	public static InputStream getInputFromOutPutStream(OutputStream out)
	    throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		baos = (ByteArrayOutputStream) out;
		ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray());
		return swapStream;
	}

}
